#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <semaphore.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <syslog.h>

#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <netinet/ip_icmp.h>
#include <errno.h>
#include <linux/route.h>
#include <netinet/in.h>
#include <netdb.h>


#include "atchannel.h"
#include "at_tok.h"
#include "plug_lte.h"
#include "hi_ext_util.h"
#include "ril.h"
#include "plug_common.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif  /* End of #ifdef __cplusplus */

static LTE_EVENT g_stEvent = {HI_NULL};

typedef struct
{
    HI_S32 signalStrength;  /* Valid values are (0-31, 99) as defined in TS 27.007 8.5 */
    HI_S32 bitErrorRate;    /* bit error rate (0-7, 99) as defined in TS 27.007 8.5 */
}LTE_SignalStrength_S;

typedef enum
{
    LTE_ZSDT_ERROR = 0,
    LTE_ZSDT_OK = 1,
    LTE_ZSDT_BUTT,
}LTE_ZSDT_STATE_E;

typedef enum
{
    LTE_NDIS_DISCONNECT = 0,
    LTE_NDIS_CONNECT = 1,
    LTE_NDIS_BUTT,
}LTE_NDIS_STATE_E;

#define NV_NDIS_0XD123_LEN 8
#define NV_NDIS_0XD123_DATA "00 00 00 03 00 00 00 02"
#define NV_NDIS_0X121_LEN 4
#define NV_NDIS_0X121_DATA "00 00 00 00"

#define AT_DELAY 200*1000

static HI_BOOL s_bLTEStart = HI_FALSE;
static HI_S32 s_fd = -1;


static HI_CHAR azLTEIface[32] = {0};

static const struct RIL_Env *s_rilenv;
static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE;

#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)

//static pthread_t s_MqttTaskId = 0;//mqtt连接的线程ID
//HI_BOOL bMqttConnecTask = HI_FALSE;//正在连接mqtt标志

HI_HAL_LTE_AT_STATE_E HI_HAL_LTE_GetATState(HI_VOID)
{	
    HI_S32 s32Ret = HI_SUCCESS;
    HI_HAL_LTE_AT_STATE_E enState = AT_ERROR;

    if (!s_bLTEStart)
    {
        return AT_ERROR;
    }

    s32Ret = at_send_command("AT", NULL);
    if (s32Ret < 0) 
    {
        enState = AT_ERROR;
        goto error;
    }
    enState = AT_OK;

    return enState;

error:
    return enState;
}

HI_HAL_LTE_NET_TYPE_E LTE_GetNetworkType(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_HAL_LTE_NET_TYPE_E enNetType = NETWORK_UNKNOWN;
    ATResponse* p_response = NULL;
    HI_CHAR *line = NULL;
    HI_CHAR *oper = NULL;
	HI_S32 cops_response[4] = {0};

	
	s32Ret = at_send_command_singleline("AT+COPS=3,2;+COPS?", "+COPS:", &p_response);
	if (0 > s32Ret || (p_response != NULL && 0 == p_response->success))
        goto error;

    line = p_response->p_intermediates->line;

    s32Ret = at_tok_start(&line);
    if (s32Ret < 0) goto error;

//+COPS:<mode>[,<format>[,<oper>][,<Act>]]
    s32Ret = at_tok_nextint(&line, &cops_response[0]);
    if (s32Ret < 0) goto error;

    if (!at_tok_hasmore(&line)) goto error;

    s32Ret = at_tok_nextint(&line, &cops_response[1]);
    if (s32Ret < 0) goto error;

    if (!at_tok_hasmore(&line)) goto error;

    s32Ret = at_tok_nextstr(&line, &oper);
    if (s32Ret < 0) goto error;
    cops_response[2] = atoi(oper);

    if (!at_tok_hasmore(&line)) goto error;

    s32Ret = at_tok_nextint(&line, &cops_response[3]);
    if (s32Ret < 0) goto error;

    switch (cops_response[3])
    {
        case 1:
        case 2:
        default:
            enNetType = NETWORK_WCDMA;
            break;
        case 7:
            enNetType = NETWORK_LTE;
            break;
        case 0:
            enNetType = NETWORK_CDMA;
            break;
    }

    if (NULL != p_response)
    {
        at_response_free(p_response);
        p_response = NULL;
    }

	return enNetType;
	
error:
    if (NULL != p_response)
    {
        at_response_free(p_response);
        p_response = NULL;
    }
	
	return enNetType;
}


/**
 * Called by atchannel when an unsolicited line appears
 * This is called on atchannel's reader thread. AT commands may
 * not be issued here
 */
//static void LTE_Unsolicited(const HI_CHAR *s, const HI_CHAR *sms_pdu)
//{
//    if (s)
//    {
//        //MLOGI("%s\n", s);
//    }
//}

static HI_BOOL LTE_Net_PingIsOk(HI_CHAR *pInterfaceName)  
{
    HI_CHAR CMD[128] ={0};
	HI_S32 s32Ret = 0;

	if (!(pInterfaceName) || !strlen(pInterfaceName))
	{
		LOGE("InterfaceName error");  
		return HI_FALSE;
	}

	sprintf(CMD,"ping %s -c 3 -I %s&> /mnt/ping_result","8.8.8.8",pInterfaceName);

	system(CMD);
    FILE *file = fopen("/mnt/ping_result","r");

    if(file == HI_NULL)
    {
        LOGE("fopen error! ");
        return HI_FALSE;
    }

    HI_CHAR cLine[512] = {0};
    
    while(fgets(cLine, sizeof(cLine), file))
    {
        if(strstr(cLine,"round-trip"))
        {
        	s32Ret = HI_TRUE;
        }
        else
        {
            s32Ret = HI_FALSE;
        }
        memset(cLine,0,sizeof(cLine));
    }
    
    fclose(file);
    
    return s32Ret;
}

static HI_S32 LTE_Get_LocalInterfaceName(HI_VOID)
{
    HI_S32 i = 0;
    HI_S32 sockfd;
    struct ifconf ifconf;
    HI_CHAR buf[512];
    struct ifreq *ifreqbuf;
    //初始化ifconf
    ifconf.ifc_len = 512;
    ifconf.ifc_buf = buf;

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        return -1;
    }

    ioctl(sockfd, SIOCGIFCONF, &ifconf);             //get all Interface information
    close(sockfd);
    
    ifreqbuf = (struct ifreq*)buf;
    for(i = 0; i < (ifconf.ifc_len / sizeof(struct ifreq)); i++)
    {
        if(!strcmp(ifreqbuf->ifr_name,LTE_PPP0))
        {
            strcpy(azLTEIface,ifreqbuf->ifr_name);
            LOGI("InterfaceName = %s",ifreqbuf->ifr_name);
            return HI_SUCCESS;
        }
        ifreqbuf++;
    }
    ifreqbuf = HI_NULL;
    return HI_FAILURE;
}


static HI_BOOL LTE_PingIsOk(HI_CHAR *pInterfaceName)
{

    HI_S32 s32Time;

    for(s32Time = 0; s32Time <= 3; s32Time++)
    {
        if(LTE_Net_PingIsOk(pInterfaceName))
        {
            LOGI("=============PING=============OK!!!\n");
            return HI_TRUE;
        }

        usleep(300 * 1000);
    }
    
    LOGE("%s Ping fail\n", pInterfaceName);

    return HI_FALSE;
}

int parse_technology_response( const char *response, int *current, int32_t *preferred )
{
    int err;
    char *line, *p;
    int ct;
    int32_t pt = 0;
    //char *str_pt;

    line = p = strdup(response);
    LOGD("Response: %s", line);
    err = at_tok_start(&p);
    if (err || !at_tok_hasmore(&p)) {
        LOGD("err: %d. p: %s", err, p);
        free(line);
        return -1;
    }

    err = at_tok_nextint(&p, &ct);
    if (err) {
        free(line);
        return -1;
    }
    if (current) *current = ct;

    LOGD("line remaining after int: %s", p);

    err = at_tok_nexthexint(&p, &pt);
    if (err) {
        free(line);
        return 1;
    }
    if (preferred) {
        *preferred = pt;
    }
    free(line);

    return 0;
}


static void onUnsolicited (const char *s, const char *sms_pdu)
{
    char *line = NULL, *p;
    int err;

    /* Ignore unsolicited responses until we're initialized.
     * This is OK because the RIL library will poll for initial state
     */
    if (sState == RADIO_STATE_UNAVAILABLE) {
        return;
    }

    if (strStartsWith(s, "%CTZV:")) {
        /* TI specific -- NITZ time */
        char *response;

        line = p = strdup(s);
        at_tok_start(&p);

        err = at_tok_nextstr(&p, &response);

        if (err != 0) {
            LOGE("invalid NITZ line %s\n", s);
        } else {
            RIL_onUnsolicitedResponse (
                RIL_UNSOL_NITZ_TIME_RECEIVED,
                response, strlen(response) + 1);
        }
        free(line);
    } else if (strStartsWith(s,"+CRING:")
                || strStartsWith(s,"RING")
                || strStartsWith(s,"NO CARRIER")
                || strStartsWith(s,"+CCWA")
    ) {
        RIL_onUnsolicitedResponse (
            RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
            NULL, 0);
#ifdef WORKAROUND_FAKE_CGEV
        RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); //TODO use new function
#endif /* WORKAROUND_FAKE_CGEV */
    } else if (strStartsWith(s,"+CREG:")
                || strStartsWith(s,"+CGREG:")
    ) {
        RIL_onUnsolicitedResponse (
            RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
            NULL, 0);
#ifdef WORKAROUND_FAKE_CGEV
        RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
#endif /* WORKAROUND_FAKE_CGEV */
    } else if (strStartsWith(s, "+CMT:")) {
        RIL_onUnsolicitedResponse (
            RIL_UNSOL_RESPONSE_NEW_SMS,
            sms_pdu, strlen(sms_pdu));
    } else if (strStartsWith(s, "+CDS:")) {
        RIL_onUnsolicitedResponse (
            RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT,
            sms_pdu, strlen(sms_pdu));
//    } else if (strStartsWith(s, "+CGEV:")) {
//        /* Really, we can ignore NW CLASS and ME CLASS events here,
//         * but right now we don't since extranous
//         * RIL_UNSOL_DATA_CALL_LIST_CHANGED calls are tolerated
//         */
//        /* can't issue AT commands here -- call on main thread */
//        RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
//#ifdef WORKAROUND_FAKE_CGEV
//    } else if (strStartsWith(s, "+CME ERROR: 150")) {
//        RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
//#endif /* WORKAROUND_FAKE_CGEV */
//    } else if (strStartsWith(s, "+CTEC: ")) {
//        int tech, mask;
//        switch (parse_technology_response(s, &tech, NULL))
//        {
//            case -1: // no argument could be parsed.
//                LOGE("invalid CTEC line %s\n", s);
//                break;
//            case 1: // current mode correctly parsed
//            case 0: // preferred mode correctly parsed
//                mask = 1 << tech;
//                if (mask != MDM_GSM && mask != MDM_CDMA &&
//                     mask != MDM_WCDMA && mask != MDM_LTE) {
//                    LOGE("Unknown technology %d\n", tech);
//                } else {
//                #ifdef RIL_ALL_ON
//                    setRadioTechnology(sMdmInfo, tech);
//                #endif
//                }
//                break;
//        }
//    } else if (strStartsWith(s, "+CCSS: ")) {
        int source = 0;
        line = p = strdup(s);
        if (!line) {
            LOGE("+CCSS: Unable to allocate memory");
            return;
        }
        if (at_tok_start(&p) < 0) {
            free(line);
            return;
        }
        if (at_tok_nextint(&p, &source) < 0) {
            LOGE("invalid +CCSS response: %s", line);
            free(line);
            return;
        }
        #ifdef RIL_ALL_ON
        SSOURCE(sMdmInfo) = source;
        #endif
        RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED,
                                  &source, sizeof(source));
    } else if (strStartsWith(s, "+WSOS: ")) {
        char state = 0;
        int unsol;
        line = p = strdup(s);
        if (!line) {
            LOGE("+WSOS: Unable to allocate memory");
            return;
        }
        if (at_tok_start(&p) < 0) {
            free(line);
            return;
        }
        if (at_tok_nextbool(&p, &state) < 0) {
            LOGE("invalid +WSOS response: %s", line);
            free(line);
            return;
        }
        free(line);

        unsol = state ?
                RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE : RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;

        RIL_onUnsolicitedResponse(unsol, NULL, 0);

    } else if (strStartsWith(s, "+WPRL: ")) {
        int version = -1;
        line = p = strdup(s);
        if (!line) {
            LOGE("+WPRL: Unable to allocate memory");
            return;
        }
        if (at_tok_start(&p) < 0) {
            LOGE("invalid +WPRL response: %s", s);
            free(line);
            return;
        }
        if (at_tok_nextint(&p, &version) < 0) {
            LOGE("invalid +WPRL response: %s", s);
            free(line);
            return;
        }
        free(line);
        RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_PRL_CHANGED, &version, sizeof(version));
    } else if (strStartsWith(s, "+CFUN: 0")) {
        setRadioState(RADIO_STATE_OFF);
    }
}


static HI_S32 AT_Init(HI_VOID)
{
    LOGE("AT_Init !!!\n");
    HI_S32 s32Ret = HI_SUCCESS;
    HI_CHAR azDevPath[32] = LTE_TTY_DEV0;
    HI_S32 s32ATCount = 0;
    HI_HAL_LTE_AT_STATE_E enATState = AT_ERROR;
    s_fd = open(azDevPath, O_RDWR);
    if (s_fd >= 0) {
        s32Ret = at_open(s_fd, onUnsolicited);
        if (s32Ret < 0) {
            LOGE("AT error %d on at_open!\n", s32Ret);
        }

        usleep(2000 * 1000);
    }

    if (s_fd < 0) {
    LOGE("Stop Exit ...\n");
    }

    for (;;)
    {
        if (!s_bLTEStart) {
            break;
        }
        s32ATCount++;
        enATState = HI_HAL_LTE_GetATState();
        if (AT_OK != enATState) {
            if (s32ATCount >= 10) {
                LOGE("AT Timeout!\n");
                break;
            }
        } else {
            s32ATCount = 0;
            LOGI("AT OK.\n");
            break;
        }
        usleep(AT_DELAY);
    }

}

static HI_VOID *LTE_StartThread(HI_VOID *param)
{
    HI_S32 s32Ret = HI_FAILURE;
    HI_S32 s32DevCount = 0; 
    HI_U32 u32Count = 0; 
    HI_BOOL ping_status;

    while (HI_TRUE == s_bLTEStart)
    {
        for(s32DevCount = 0;s32DevCount<10;s32DevCount++)
        {

            LOGI("DevCount:%d\n",s32DevCount);

            if ((0 == access(LTE_TTY_DEV0, 0)) && (0 == access(LTE_TTY_DEV1, 0)) 
                && (0 == access(LTE_TTY_DEV2, 0))) 
            {

                break;
            }
            else
            {
                usleep(1000 * 1000);
                continue;
            }
        }

        if(s32DevCount >= 10)
        {
            LOGE("usb error!\n");
            break;
        }
        
        for(u32Count = 0;u32Count<5;u32Count++)
        {
            system("pppd call gprs-dial &");
            usleep(8000 * 1000);
            LOGI("pppd call gprs-dia [%d]\n",u32Count);
            s32Ret = LTE_Get_LocalInterfaceName();
            if(s32Ret == HI_FAILURE)
            {
                LOGI("LTE_Get_LocalInterfaceName FAILURE!!!\n");
                continue;
            }
            else
            {
                break;
            }
        }
        
        if(u32Count >= 5)
        {
            LOGE("pppd Dialing error !\n");
            break;
        }
        
        system("cp /etc/ppp/resolv.conf /etc");

        ping_status = LTE_PingIsOk(azLTEIface);
        if(HI_FALSE == ping_status)
        {
            LOGE("network ping error !\n");
            
        }
        else
        {
//            system("del default");
//            system("route add default ppp0");
//            #if (defined(HI_COMM_AI_ADAPT_GESTURE))
//            s32Ret = PLUG_COMM_GESTURE_Init();
//            HI_APPCOMM_CHECK_EXPR((HI_SUCCESS == s32Ret), HI_FAILURE);
//            #else
//            if (HI_FALSE == bMqttConnecTask)
//            {
//                bMqttConnecTask = HI_TRUE;
//                extern HI_VOID* PLUG_COMMON_MQTT_Init(HI_VOID);
//                s32Ret = pthread_create(&s_MqttTaskId, HI_NULL, PLUG_COMMON_MQTT_Init, HI_NULL);
//                if (HI_SUCCESS != s32Ret)
//                {
//                    LOGE("pthread create PLUG_COMMON_MQTT_Init failed!\n");
//                }
//            }
//            #endif
            if(g_stEvent.pfnEvent != HI_NULL)
            {
                g_stEvent.pfnEvent(HI_TRUE);
            }

        }
        break;
    }

    return HI_NULL;
}


HI_S32 HI_HAL_LTE_Init(LTE_EVENT *pEvent)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    pthread_t threadStart;
    LOGI("============ HI_HAL_LTE_Init start=============\n");
    if (s_bLTEStart)
    {
        LOGE("LTE is started.\n");
        return HI_FAILURE;
    }
    memcpy(&g_stEvent, pEvent, sizeof(LTE_EVENT));
    s_bLTEStart = HI_TRUE;
    AT_Init();

    s32Ret = pthread_create(&threadStart, NULL, LTE_StartThread, NULL);
    s32Ret |= pthread_detach(threadStart);
    if (s32Ret < 0)
    {
        LOGE("pthread_create LTE_StartThread fail! %#x\n", s32Ret);
        return HI_FAILURE;
    }

    return s32Ret;
}
